using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using Data.Merge;
using Data;
using Data.Probabilities;
using System.Windows.Forms;
using Data.DataTranslator;
using System.Threading;

namespace Logic.Agglomerative
{
    //delete this row
    class Agglomerative
    {
        IAgglomerative data;
        ArrayList mergeOrd;     //will collect the order of the merging
        int maxIterations;
        ProbabilityData ProbData;
        #region Constructors

        public Agglomerative(IAgglomerative dataHolder,ProbabilityData newProbData)
        {
            ProbData = newProbData;
            data = dataHolder;
            mergeOrd = new ArrayList();
            maxIterations = ProbData.NumberOfGenomes;
        }

        #endregion

        #region Properties
        
        public int MaxIteration
        {
            set
            {
                if (ProbData.NumberOfGenomes >= value)
                {
                    maxIterations = value;
                }
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// perform the agglomerative algorithm using the distance matrix we get as input
        /// in the AVL trees generate a tree of merges in an array in post order form.
        /// </summary>
        /// <param name="mergeType">the type of the merge function
        /// 1-Complete linkage
        /// 2-Single linkage
        /// 3-AVERAGE linkage</param>
        /// <returns>array list of the merges in post order each cell contain
        /// its name and its left and right sons names </returns>
        //         public ArrayList runAgglomerative(int mergeType,int numOfClusters,ProgressBar prog)
        //{
        //    if (numOfClusters < 1)
        //        numOfClusters = 1;
        //    //generate MergeSteps for the leafs
        //    for (int i = 0; i < maxIterations; i++)
        //    {
        //        MergeStep curMerge = new MergeStep(i, -1, -1, 0);
        //        mergeOrd.Add(curMerge); //add to the merge list
        //    }
        //    int clusterId = maxIterations;
        //    for (int i = maxIterations - numOfClusters + 1; i > 1; i--)
        //    {
        //        Node min = data.getMin();  //find the minimal distance in the distance matrix
        //        //we merge the two indices and save the left the right and the new one
        //        int newHeight = 1 + Math.Max(((MergeStep)mergeOrd[min.I]).Hight, ((MergeStep)mergeOrd[min.J]).Hight);

        //        MergeStep curMerge = new MergeStep(clusterId, min.I, min.J, newHeight);
        //        clusterId++;
        //        mergeOrd.Add(curMerge); //add to the merge list
        //        //dispose the old clusters and create a new one
        //        data.merge(min.I, min.J, mergeType, curMerge.Name); //merge the two avl trees and save the new one
        //        //data.ColAvlList = min.I;
        //        //data.ColAvlList = min.J;
        //        ((MergeStep)mergeOrd[min.I]).IsRoot = false;
        //        ((MergeStep)mergeOrd[min.J]).IsRoot = false;
        //    }
        //    return this.Rearange(numOfClusters);
        //}
        public ArrayList runAgglomerative(int mergeType,int numOfClusters)
        {
            if (numOfClusters < 1)
                numOfClusters = 1;
            //generate MergeSteps for the leafs
            for (int i = 0; i < maxIterations; i++)
            {
                MergeStep curMerge = new MergeStep(i, -1, -1, 0);
                mergeOrd.Add(curMerge); //add to the merge list
            }
            int clusterId = maxIterations;
            kDimMap.aggProgBar = 0;
            for (int i = maxIterations - numOfClusters ; i >= 1; i--)
            {
                Node min = data.getMin();  //find the minimal distance in the distance matrix
                //we merge the two indices and save the left the right and the new one
                int newHeight = 1 + Math.Max(((MergeStep)mergeOrd[min.I]).Hight, ((MergeStep)mergeOrd[min.J]).Hight);

                MergeStep curMerge = new MergeStep(clusterId, min.I, min.J, newHeight);
                clusterId++;
                mergeOrd.Add(curMerge); //add to the merge list
                //dispose the old clusters and create a new one
                data.merge(min.I, min.J, mergeType, curMerge.Name); //merge the two avl trees and save the new one
                //data.ColAvlList = min.I;
                //data.ColAvlList = min.J;
                ((MergeStep)mergeOrd[min.I]).IsRoot = false;
                ((MergeStep)mergeOrd[min.J]).IsRoot = false;
                kDimMap.aggProgBar = kDimMap.aggProgBar + ((double)(100)/(double)(maxIterations - numOfClusters));
                Thread.Sleep(19);
            }
            kDimMap.aggProgBar = 100;
            Thread.Sleep(19);
            return this.Rearange(numOfClusters);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        internal ArrayList Rearange(int numberOfClusters )
        {
            ArrayList PostMergeOrder;
            ArrayList finalMergeOrder = new ArrayList();
            int clusterKey = 0;
            foreach (MergeStep merge in mergeOrd)
            {
                PostMergeOrder = new ArrayList();
                if (merge.IsRoot == true)
                {
                    PostOrder((MergeStep)mergeOrd[mergeOrd.IndexOf(merge)], PostMergeOrder, mergeOrd, numberOfClusters - 1, clusterKey);
                    clusterKey++;
                }
                finalMergeOrder.AddRange(PostMergeOrder);
            }
            return finalMergeOrder;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="merge"></param>
        /// <param name="PostMergeOrder"></param>
        /// <param name="mergeList"></param>
        /// <param name="newDepth"></param>
        internal void PostOrder(MergeStep merge, ArrayList PostMergeOrder, ArrayList mergeList, int newDepth,int clusterKey)
        {
            if (merge.hasLeftSon())
                PostOrder((MergeStep)mergeList[merge.Leftson], PostMergeOrder, mergeList, 1 + newDepth, clusterKey);
            if (merge.hasRightSon())
                PostOrder((MergeStep)mergeList[merge.Rightson], PostMergeOrder, mergeList, 1 + newDepth, clusterKey);
            merge.Depth = newDepth;
            merge.ClusterNum = clusterKey;
            PostMergeOrder.Add(merge);
        }

        public ArrayList ClustersMembers()
        {
            ArrayList clusters = new ArrayList();
            foreach (MergeStep step in mergeOrd)
            {
                if (step.isLeaf())
                {
                    clusters[step.Name] = step.ClusterNum;
                }
            }
            return clusters;
        }

        #endregion

    }
}